﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

//
#pragma once
#include "GnufBase.h"

// Implementation //////////////////////////////////////////////////////////////////////////////////
namespace gnuf { class ISingleWorkerThreadObserver; }
namespace gnuf { namespace impl {
    class SingleWorkerThreadImpl final
    {
    public:
        SingleWorkerThreadImpl();
        ~SingleWorkerThreadImpl();
        
        enum class EWorkType : std::uint8_t {
            eIdle,
            eUnitTest,
            eProgressBar,
            eProgressBoard
        };
        
        typedef std::function< void() > Work;
        
        bool execute(const SingleWorkerThreadImpl::EWorkType,
                     const SingleWorkerThreadImpl::Work &);
        
        bool isExecuting() const { return m_executing; }
        bool isIdle()      const { return SingleWorkerThreadImpl::EWorkType::eIdle == m_workType; }
        bool isBusy()      const { return SingleWorkerThreadImpl::EWorkType::eIdle != m_workType; }
        
        typedef std::list< std::pair< std::string,
            gcmp::WeakPtr< ISingleWorkerThreadObserver > > > Observers;
            
        size_t numberOfObservers() const { return m_observers.size(); }
        const ISingleWorkerThreadObserver * observerOf(const std::string & id) const;
        bool    addObserver     (const std::string & id, ISingleWorkerThreadObserver * pObserver);
        bool insertObserver     (const std::string & id, ISingleWorkerThreadObserver * pObserver,
                                 const std::string & posId);
        bool insertObserver     (const std::string & id, ISingleWorkerThreadObserver * pObserver,
                                 const ISingleWorkerThreadObserver * pPosObserver);
        bool removeObserverOf   (const std::string & id);
        bool removeObserver     (const ISingleWorkerThreadObserver * pObserver);
            
    private:
        void beginToNotify(std::vector< ISingleWorkerThreadObserver * > &, size_t &);
        void   endToNotify(const size_t);
        void        notify(void (ISingleWorkerThreadObserver::*fire)());
        template <class TFunction>
        void        notify(const TFunction & func)
        {
            const size_t count(m_observers.size());
            if (0 == count)
                return; // break, if there's not any observer

            std::vector< ISingleWorkerThreadObserver * > ones(count, nullptr);
            size_t countInvalid(0);
            beginToNotify(ones, countInvalid);  // begin
            for (size_t i = 0; count > countInvalid && i < ones.size(); ++i /* next */) {
                if (nullptr != ones[ i ])
                    func(ones[ i ]);            // notify
            }
            endToNotify(countInvalid);          // end
        }
        
        SingleWorkerThreadImpl::EWorkType m_workType = SingleWorkerThreadImpl::EWorkType::eIdle;
        bool m_executing = false;
        
        SingleWorkerThreadImpl::Observers m_observers;
    };
}}

// Interface ///////////////////////////////////////////////////////////////////////////////////////
namespace gnuf {
    class GNUF_BASE_EXPORT ISingleWorkerThreadObserver : public gcmp::WeakReferenceable
    {
        friend class impl::SingleWorkerThreadImpl;
    public:
        virtual ~ISingleWorkerThreadObserver() {}
        
        ///\return Return true, if observing now.
        bool isObserving() const { return m_observing; }

        ///\brief Override to get a callback before started.
        virtual void onWorkPreStarted() {}
        
        ///\brief Override to get a callback after finished.
        ///\param result: the executed result.
        virtual void onWorkPostFinished(bool result) {}
        
    private:
        bool m_observing = false;
    };
    
    class GNUF_BASE_EXPORT SingleWorkerThread final
    {
    public:
        // Cannot copy or assign.
        SingleWorkerThread(const SingleWorkerThread &) = delete;
        SingleWorkerThread& operator=(const SingleWorkerThread &) = delete;

        ///\return Return the SingleWorkerThread reference.
        static SingleWorkerThread & rGet();
        
        ///\return Return true, if executing.
        bool isExecuting() const { return m_impl.isExecuting(); }
        
        ///\return Return true, if idle.
        bool isIdle() const { return m_impl.isIdle(); }
        
        ///\return Return true, if busy (i.e. executing or notifying).
        bool isBusy() const { return m_impl.isBusy(); }
            
        ///\return Return true if there's any observer.
        size_t numberOfObservers() const
        { return m_impl.numberOfObservers(); }
        
        ///\brief Get the obserer by the Id.
        ///\param id: the observer's Id.
        ///\return Return nullptr if not existed.
        const ISingleWorkerThreadObserver * observerOf(const std::string & id) const
        { return m_impl.observerOf(id); }
        
        ///\brief Add an observer at the end.
        ///\param        id: the observer's Id.
        ///\param pObserver: the pointer to the observer.
        ///\return Return true if successful.
        bool addObserver(const std::string & id, ISingleWorkerThreadObserver * pObserver)
        { return m_impl.addObserver(id, pObserver); }
        
        ///\brief Insert an observer.
        ///\param        id: the observer's Id.
        ///\param pObserver: the pointer to the observer.
        ///\param     posId: the Id of the observer at the position.
        ///\return Return ture if successful.
        bool insertObserver(const std::string & id, ISingleWorkerThreadObserver * pObserver,
                            const std::string & posId)
        { return m_impl.insertObserver(id, pObserver, posId); }
        
        ///\brief Inserta an observer.
        ///\param           id: the observer's Id.
        ///\param    pObserver: the pointer to the observer.
        ///\param pPosObserver: the pointer to the observer at the position.
        bool insertObserver(const std::string & id, ISingleWorkerThreadObserver * pObserver,
                            const ISingleWorkerThreadObserver * pPosObserver)
        { return m_impl.insertObserver(id, pObserver, pPosObserver); }
        
        ///\brief Remove the observer.
        ///\param id: the observer's Id.
        ///\return Return true if successful.
        bool removeObserverOf(const std::string & id)
        { return m_impl.removeObserverOf(id); }
        
        ///\brief Remove the observer.
        ///\param pObserver: the pointer to the observer.
        ///\return Return true if successful.
        bool removeObserver(const ISingleWorkerThreadObserver * pObserver)
        { return m_impl.removeObserver(pObserver); }
        
    public:
        impl::SingleWorkerThreadImpl & rImpl() { return m_impl; }

    private:
        SingleWorkerThread();
        ~SingleWorkerThread();
        
        impl::SingleWorkerThreadImpl m_impl;
    };
}